package com.zjj.lbw.ai;

import com.alibaba.cloud.ai.advisor.RetrievalRerankAdvisor;
import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeConnectionProperties;
import com.alibaba.cloud.ai.dashscope.agent.DashScopeAgent;
import com.alibaba.cloud.ai.dashscope.agent.DashScopeAgentOptions;
import com.alibaba.cloud.ai.dashscope.api.DashScopeAgentApi;
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.rag.DashScopeDocumentRetrievalAdvisor;
import com.alibaba.cloud.ai.dashscope.rag.DashScopeDocumentRetriever;
import com.alibaba.cloud.ai.dashscope.rag.DashScopeDocumentRetrieverOptions;
import com.alibaba.cloud.ai.evaluation.AnswerFaithfulnessEvaluator;
import com.alibaba.cloud.ai.model.RerankModel;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.document.Document;
import org.springframework.ai.evaluation.EvaluationRequest;
import org.springframework.ai.evaluation.EvaluationResponse;
import org.springframework.ai.image.ImageModel;
import org.springframework.ai.image.ImageOptionsBuilder;
import org.springframework.ai.image.ImagePrompt;
import org.springframework.ai.image.ImageResponse;
import org.springframework.ai.rag.retrieval.search.DocumentRetriever;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/alibaba")
public class ChatAlibabaController {

    private final ChatClient chatClient;

    public ChatAlibabaController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder
                .build();
    }

    @Autowired
    private VectorStore vectorStore;

    @Autowired
    private RerankModel rerankModel;

    @Autowired
    private ImageModel imageModel;

    @Resource
    private ChatModel chatModel;

    @Autowired
    private DocumentService documentService;

    @Autowired
    private DashScopeConnectionProperties dashScopeConnectionProperties;


    @GetMapping("/chat")
    public String chat() {
        return this.chatClient.prompt()
                .user("你是谁")
                .call()
                .content();
    }

    @GetMapping("/rankChat")
    public String rankChat(@RequestParam("message") String message) {
        return this.chatClient.prompt()
                .user(message)
                .advisors(new RetrievalRerankAdvisor(vectorStore, rerankModel))
                .call()
                .content();
    }


    @GetMapping("/imageChat")
    public String multimodalChat(@RequestParam("message") String message) {
        ImagePrompt imagePrompt = new ImagePrompt(message, ImageOptionsBuilder.builder().build());
        ImageResponse response = imageModel.call(imagePrompt);
        return response.getResult().getOutput().getUrl();
    }


    @GetMapping("/evaluation")
    public EvaluationResponse evaluation(@RequestParam String message) {

        // 向量搜索
        List<Document> documentList = documentService.search(message);

        // 提示词模板
        PromptTemplate promptTemplate = new PromptTemplate("{userMessage}\n\n 用以下信息回答问题:\n {contents}");

        // 组装提示词
        Prompt prompt = promptTemplate.create(Map.of("userMessage", message, "contents", documentList));

        // 调用大模型
        String result = chatClient.prompt(prompt).call().content();

        String DEFAULT_EVALUATION_PROMPT_TEXT = """
			您是一名评测专家，能够基于提供的评分标准和内容信息进行评分。
			您将获得一些FACTS(事实内容)和STUDENT ANSWER。

			以下是评分标准：
			(1) 确保STUDENT ANSWER的内容是基于FACTS的事实内容，不能随意编纂。
			(2) 确保STUDENT ANSWER的内容没有超出FACTS的内容范围外的虚假信息。

			Score:
			得分为1意味着STUDENT ANSWER满足所有标准。这是最高（最佳）得分。
			得分为0意味着STUDENT ANSWER没有满足所有标准。这是最低的得分。

			请逐步解释您的推理，以确保您的推理和结论正确，避免简单地陈述正确答案。

			最终答案按照标准的json格式输出,不要使用markdown的格式:
			{"score": 0.7, "feedback": "STUDENT ANSWER的内容超出了FACTS的事实内容。"}

			FACTS: {context}
			STUDENT ANSWER: {student_answer}
			""";

        var relevancyEvaluator = new AnswerFaithfulnessEvaluator(ChatClient.builder(chatModel), DEFAULT_EVALUATION_PROMPT_TEXT, new ObjectMapper());

        // 评估是否产生了幻觉
        EvaluationRequest evaluationRequest = new EvaluationRequest(message, documentList, result);

        EvaluationResponse evaluationResponse = relevancyEvaluator.evaluate(evaluationRequest);

        return evaluationResponse;
    }

    @GetMapping("/rag")
    public String rag(@RequestParam("message") String message) {

        String indexName = "rag-index";

        DashScopeApi dashScopeApi = new DashScopeApi(dashScopeConnectionProperties.getApiKey());

        DocumentRetriever retriever = new DashScopeDocumentRetriever(dashScopeApi,
                DashScopeDocumentRetrieverOptions.builder().withIndexName(indexName).build());

        return chatClient.prompt()
                .advisors(new DashScopeDocumentRetrievalAdvisor(retriever, true))
                .user(message)
                .call()
                .content();
    }


    @Value("${spring.ai.dashscope.agent.app-id}")
    private String appId;

    @Autowired
    private DashScopeAgentApi dashscopeAgentApi;

    @GetMapping("/agent")
    public String agent(@RequestParam(value = "message") String message) {
        DashScopeAgent agent = new DashScopeAgent(dashscopeAgentApi);
        ChatResponse response = agent.call(new Prompt(message, DashScopeAgentOptions.builder().withAppId(appId).build()));
        AssistantMessage app_output = response.getResult().getOutput();
        return app_output.getText();
    }
}
